﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.Net;

namespace SharedGenomics.Workbench.Core
{
    public class UserCredentials
    {
        #region Private Members

        private string userName;
        private SecureString password;
        private string domain;

        #endregion

        #region Constructors

        public UserCredentials(string userName, SecureString password)
        {
            this.UserName = userName;
            this.Password = password;
        }

        #endregion

        #region Public Methods

        public void ApplyToWebServiceProxy(System.Web.Services.Protocols.SoapHttpClientProtocol proxy)
        {
            CredentialCache credentialCache = new CredentialCache();

            // Add the NetworkCredential to the CredentialCache.
            credentialCache.Add(new Uri(proxy.Url), "NTLM", this.GetNetworkCredential());

            // Add the CredentialCache to the proxy class credentials.
            proxy.UseDefaultCredentials = false;
            proxy.Credentials = credentialCache;
        }

        public void ApplyToWCFProxy<T>(System.ServiceModel.ClientBase<T> proxy) where T : class
        {
            proxy.ClientCredentials.Windows.ClientCredential.Domain = this.Domain;
            proxy.ClientCredentials.Windows.ClientCredential.UserName = this.UserName;

            IntPtr ptr = Marshal.SecureStringToBSTR(this.Password);
            try
            {
                proxy.ClientCredentials.Windows.ClientCredential.Password = Marshal.PtrToStringAuto(ptr);
            }
            finally
            {
                Marshal.ZeroFreeBSTR(ptr);
            }
        }

        public NetworkCredential CreateNetworkCredential()
        {
            IntPtr ptr = Marshal.SecureStringToBSTR(this.Password);
            NetworkCredential credentials;

            try
            {
                credentials = new NetworkCredential(this.UserName, Marshal.PtrToStringAuto(ptr), this.Domain);
            }
            finally
            {
                Marshal.ZeroFreeBSTR(ptr);
            }

            return credentials;
        }

        #endregion

        #region Private Methods

        private NetworkCredential GetNetworkCredential()
        {
            NetworkCredential nc = null;

            //get the password from the secure string - make sure the resulting managed string is cleared after use (although since it hangs around in 
            //plain text in the BITS credential object anyway...)
            IntPtr ptr = Marshal.SecureStringToBSTR(this.Password);

            try
            {
                nc = (this.Domain == null) ?
                    new NetworkCredential(this.UserName, Marshal.PtrToStringAuto(ptr)) :
                    new NetworkCredential(this.UserName, Marshal.PtrToStringAuto(ptr), this.Domain);
            }
            finally
            {
                Marshal.ZeroFreeBSTR(ptr);
            }

            return nc;
        }

        #endregion

        #region Properties

        public string UserName
        {
            get { return this.userName; }
            private set
            {
                if (value == null)
                    throw new ArgumentNullException("UserName");

                string[] userNameSections = value.Split('\\');

                if (userNameSections.Length == 1)
                {
                    this.userName = value;
                }
                else if (userNameSections.Length == 2)
                {
                    this.Domain = userNameSections[0];
                    this.userName = userNameSections[1];
                }
                else
                {
                    throw new ArgumentException(String.Format("Invalid user name format: '{0}' - user names should be of the form 'Username' or 'Domain\\Username'", value));
                }
            }
        }

        public string Domain
        {
            get { return this.domain; }
            private set { this.domain = value; }
        }

        public SecureString Password
        {
            get { return this.password; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("Password");

                this.password = value;
            }
        }

        #endregion
    }
}
